  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="generator" content="pandoc" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <title>GTK 4 tutorial</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
      ul.task-list{list-style: none;}
      pre{overflow: visible;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::after
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {   }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span.al { color: #ff0000; font-weight: bold; } /* Alert */
      code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
      code span.at { color: #7d9029; } /* Attribute */
      code span.bn { color: #40a070; } /* BaseN */
      code span.bu { } /* BuiltIn */
      code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
      code span.ch { color: #4070a0; } /* Char */
      code span.cn { color: #880000; } /* Constant */
      code span.co { color: #60a0b0; font-style: italic; } /* Comment */
      code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
      code span.do { color: #ba2121; font-style: italic; } /* Documentation */
      code span.dt { color: #902000; } /* DataType */
      code span.dv { color: #40a070; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #40a070; } /* Float */
      code span.fu { color: #06287e; } /* Function */
      code span.im { } /* Import */
      code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
      code span.kw { color: #007020; font-weight: bold; } /* Keyword */
      code span.op { color: #666666; } /* Operator */
      code span.ot { color: #007020; } /* Other */
      code span.pp { color: #bc7a00; } /* Preprocessor */
      code span.sc { color: #4070a0; } /* SpecialChar */
      code span.ss { color: #bb6688; } /* SpecialString */
      code span.st { color: #4070a0; } /* String */
      code span.va { color: #19177c; } /* Variable */
      code span.vs { color: #4070a0; } /* VerbatimString */
      code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
      div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
      table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
      th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
      td {padding: 2px 6px; border: 1px solid;}
      img {display: block; margin-left: auto; margin-right: auto;}
      figcaption {text-align: center;}
    </style>
  </head>
  <body style="padding-top: 70px;">
    <div class="container">
    <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
      <div class="container-fluid">
        <span class="navbar-brand">Gtk4 tutorial</span>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
          <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec19.html">Prev: section19</a>
</li>

            <li class="nav-item">
<a class="nav-link" href="sec21.html">Next: section21</a>
</li>

          </ul>
        </div>
      </div>
    </nav>
<h1
id="gtkmenubutton-accelerators-font-pango-and-gsettings">GtkMenuButton,
accelerators, font, pango and gsettings</h1>
<p>Tfe text editor will be restructured in this section.</p>
<ul>
<li>Open, save and close buttons are placed on the toolbar. In addition,
GtkMenuButton is added to the toolbar. This button shows a popup menu
when clicked on. Here, popup means widely, including pull-down
menu.</li>
<li>New, save-as, preference and quit items are put into the menu.</li>
</ul>
<p>This makes the most frequently used operation bound to the tool bar
buttons. And the others are stored in behind the menus. So, it is more
practical.</p>
<p>In addition, the following features are added.</p>
<ul>
<li>Accelerators. For example, Ctrl-O reads a file and creates a new
page.</li>
<li>Preference dialog for font selection.</li>
<li>Alert dialog to confirm closing or quitting without saving
contents.</li>
<li>GSettings to keep the font selection.</li>
</ul>
<figure>
<img src="image/tfe6.png" alt="tfe6" />
<figcaption aria-hidden="true">tfe6</figcaption>
</figure>
<h2 id="static-variables-shared-by-functions-in-tfeapplication.c">Static
variables shared by functions in tfeapplication.c</h2>
<p>The next version of <code>tfe</code> has static variables in
<code>tfeapplication.c</code>. Static variables are convenient but not
good for maintenance. So, the final version will remove them and take
another way to cover the static variables.</p>
<p>Anyway, the following is the code with regard to the static
variables.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkDialog <span class="op">*</span>pref<span class="op">;</span> <span class="co">// preference dialog</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkFontButton <span class="op">*</span>fontbtn<span class="op">;</span> <span class="co">// font button</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GSettings <span class="op">*</span>settings<span class="op">;</span> <span class="co">// GSetting</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkDialog <span class="op">*</span>alert<span class="op">;</span> <span class="co">// alert dialog</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkLabel <span class="op">*</span>lb_alert<span class="op">;</span>  <span class="co">// label in the alert dialog</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkButton <span class="op">*</span>btn_accept<span class="op">;</span> <span class="co">// accept button in the alert dialog</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkCssProvider <span class="op">*</span>provider0<span class="op">;</span> <span class="co">//CSS provider for textview padding</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GtkCssProvider <span class="op">*</span>provider<span class="op">;</span> <span class="co">// CSS provider for fonts</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong pref_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong alert_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean is_quit<span class="op">;</span> <span class="co">// flag whether to quit or close</span></span></code></pre></div>
<p>These variables can be referred by any functions in the file.</p>
<h2 id="signal-tags-in-ui-files">Signal tags in ui files</h2>
<p>The four buttons are included in the ui file <code>tfe.ui</code>. A
difference from prior sections is signal tags. The following is
extracted from <code>tfe.ui</code> and it describes the open button.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btno&quot;</span>&gt;</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Open&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;clicked&quot;</span><span class="ot"> handler=</span><span class="st">&quot;open_cb&quot;</span><span class="ot"> swapped=</span><span class="st">&quot;TRUE&quot;</span><span class="ot"> object=</span><span class="st">&quot;nb&quot;</span>&gt;&lt;/<span class="kw">signal</span>&gt;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<p>Signal tag specifies the name of the signal, handler and user_data
object.</p>
<ul>
<li>The signal name is “clicked”.</li>
<li>The handler is “open_cb”.</li>
<li>The user data object is “nb” (GtkNoteBook instance).</li>
</ul>
<p>Swapped attribute has the same effect as
<code>g_signal_connect_swapped</code> function. So, the signal tag above
works the same as:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>g_signal_connect_swapped <span class="op">(</span>btno<span class="op">,</span> <span class="st">&quot;clicked&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>open_cb<span class="op">),</span> nb<span class="op">);</span></span></code></pre></div>
<p>This function swaps the button and the forth argument
(<code>btno</code> and <code>nb</code>) in the handler. If
<code>g_signal_connect</code> is used, the handler is like this:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* The parameter user_data is assigned with nb */</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>open_cb <span class="op">(</span>GtkButton <span class="op">*</span>btno<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span> <span class="op">...</span> <span class="op">...</span> <span class="op">}</span></span></code></pre></div>
<p>If <code>g_signal_connect_swapped</code> is used, the button and the
user data are swapped.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* btno and user_data (nb) are exchanged */</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>open_cb <span class="op">(</span>GtkNoteBook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span> <span class="op">...</span> <span class="op">...</span> <span class="op">}</span></span></code></pre></div>
<p>It is good if the button instance is useless in the handler.</p>
<p>When you use a signal tag in your ui file, you need “-WI,
–export-dynamic” options to compile. You can achieve this by adding
“export_dynamic: true” argument to executable function in
<code>meson.build</code>. And remove static class from the handler.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>open_cb <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  notebook_page_open <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>If you add static, the function is in the scope of the file and it
can’t be seen from outside. Then the signal tag can’t find the
function.</p>
<h2 id="menu-and-gkmenubutton">Menu and GkMenuButton</h2>
<p>Traditional menu structure is fine. However, We don’t use all the
menus or buttons so often. Some mightn’t be clicked at all. Therefore,
it’s a good idea to put some frequently used buttons on the toolbar and
the rest into the menu. Such menu are often connected to
GtkMenuButton.</p>
<p>Menus are described in <code>menu.ui</code> file.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/menu.ui @@@</p>
<p>There are four items, “New”, “Saveas”, “Preference” and “Quit”.</p>
<ul>
<li>“New” menu creates a new empty page.</li>
<li>“Saveas” menu saves the current page as a different filename from
the original one.</li>
<li>“Preference” menu sets preference items. This version of
<code>tfe</code> has only font preference.</li>
<li>“Quit” menu quits the application.</li>
</ul>
<p>These four menus are not used so often. That’s why they are put into
the menu behind the menu button.</p>
<p>All the actions above have “win” scope. Tfe has only one window even
if the second application runs. So, the scope “app” and “win” have very
little difference in this application.</p>
<p>The menus and the menu button are connected with
<code>gtk_menu_button_set_menu_model</code> function. The variable
<code>btnm</code> below points a GtkMenuButton object.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>  build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/tfe/menu.ui&quot;</span><span class="op">);</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  menu <span class="op">=</span> G_MENU_MODEL <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;menu&quot;</span><span class="op">));</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  gtk_menu_button_set_menu_model <span class="op">(</span>btnm<span class="op">,</span> menu<span class="op">);</span></span></code></pre></div>
<h2 id="actions-and-accelerators">Actions and Accelerators</h2>
<p>Menus are connected to actions. Actions are defined with an array and
<code>g_action_map_add_action_entries</code> function.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>  <span class="dt">const</span> GActionEntry win_entries<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;open&quot;</span><span class="op">,</span> open_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;save&quot;</span><span class="op">,</span> save_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;close&quot;</span><span class="op">,</span> close_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;new&quot;</span><span class="op">,</span> new_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;saveas&quot;</span><span class="op">,</span> saveas_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;pref&quot;</span><span class="op">,</span> pref_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">},</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;close-all&quot;</span><span class="op">,</span> close_all_activated<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">,</span> NULL <span class="op">}</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>  g_action_map_add_action_entries <span class="op">(</span>G_ACTION_MAP <span class="op">(</span>win<span class="op">),</span> win_entries<span class="op">,</span> G_N_ELEMENTS <span class="op">(</span>win_entries<span class="op">),</span> nb<span class="op">);</span></span></code></pre></div>
<p>There are seven actions, open, save, close, new, saveas, pref and
close-all. But there were only four menus. New, saveas, pref and
close-all actions correspond to new, saveas, preference and quit menu
respectively. The three actions open, save and close doesn’t have
corresponding menus. Are they necessary? Yes, because they correspond to
accelerators.</p>
<p>Accelerators are kinds of short cut key functions. They are defined
with arrays and <code>gtk_application_set_accels_for_action</code>
function.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>action<span class="op">;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>accels<span class="op">[</span><span class="dv">2</span><span class="op">];</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span> action_accels<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;win.open&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Control&gt;o&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;win.save&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Control&gt;s&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;win.close&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Control&gt;w&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;win.new&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Control&gt;n&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;win.saveas&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Shift&gt;&lt;Control&gt;s&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span> <span class="st">&quot;win.close-all&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Control&gt;q&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>  <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> G_N_ELEMENTS<span class="op">(</span>action_accels<span class="op">);</span> i<span class="op">++)</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a>    gtk_application_set_accels_for_action<span class="op">(</span>GTK_APPLICATION<span class="op">(</span>app<span class="op">),</span> action_accels<span class="op">[</span>i<span class="op">].</span>action<span class="op">,</span> action_accels<span class="op">[</span>i<span class="op">].</span>accels<span class="op">);</span></span></code></pre></div>
<p>This code is a bit complicated. The array
<code>action-accels[]</code> is an array of structures. The structure
is:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>action<span class="op">;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">const</span> <span class="dt">char</span> <span class="op">*</span>accels<span class="op">[</span><span class="dv">2</span><span class="op">];</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span></code></pre></div>
<p>The member <code>action</code> is a string. The member
<code>accels</code> is an array of two strings. For example,</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span> <span class="st">&quot;win.open&quot;</span><span class="op">,</span> <span class="op">{</span> <span class="st">&quot;&lt;Control&gt;o&quot;</span><span class="op">,</span> NULL <span class="op">}</span> <span class="op">},</span></span></code></pre></div>
<p>This is the first element of the array
<code>action_accels</code>.</p>
<ul>
<li>The member <code>action</code> is “win.open”. This specifies the
action “open” belongs to the window object.</li>
<li>The member <code>accels</code> is an array of two strings,
“&lt;Control&gt;o” and NULL. The first string specifies a key
combination. Control key and ‘o’. If you keep pressing the control key
and push ‘o’ key, then it activates the action <code>win.open</code>.
The second string NULL (or zero) means the end of the list (array). You
can define more than one accelerator keys and the list must ends with
NULL (zero). If you want to do so, the array length needs to be three or
more. The parser recognizes “&lt;control&gt;o”,
“&lt;Shift&gt;&lt;Alt&gt;F2”, “&lt;Ctrl&gt;minus” and so on. If you want
to use symbol key like “&lt;Ctrl&gt;-”, use “&lt;Ctrl&gt;minus” instead.
Such relation between lower case and symbol (character code) is
specified in <a
href="https://gitlab.gnome.org/GNOME/gtk/-/blob/master/gdk/gdkkeysyms.h"><code>gdkkeysyms.h</code></a>
in the GTK 4 source code.</li>
</ul>
<h3 id="open-save-and-close-handlers">Open, save and close handlers</h3>
<p>There are two open handlers. One is a handler for the clicked signal
on the button. The other is for the activate signal on the action.</p>
<pre><code>Open button ==(clicked)==&gt; open.cb handler
Ctrl-o key (accerelator) ==(key down)==&gt; open action activated ==&gt; open_activated handler</code></pre>
<p>But the behavior of the two handlers are the same. So,
<code>open_activate</code> just call <code>open.cb</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/tfeapplication.c open.cb open_activated @@@</p>
<p>The same goes on with the save and close handlers.</p>
<h2 id="saveas-handler">Saveas handler</h2>
<p>TfeTextView has a saveas function. So we just write a wrapper
function in <code>tfenotebook.c</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/tfenotebook.c get_current_textview notebook_page_saveas @@@</p>
<p>The function <code>get_current_textview</code> is the same as before.
The function <code>notebook_page_saveas</code> simply calls
<code>tfe_text_view_saveas</code>.</p>
<p>In <code>tfeapplication.c</code>, saveas handler just call
<code>notebook_page_saveas</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/tfeapplication.c saveas_activated @@@</p>
<h2 id="preference-and-alert-dialog">Preference and alert dialog</h2>
<h3 id="preference-dialog">Preference dialog</h3>
<p>Preference dialog xml definition is added to <code>tfe.ui</code>.</p>
<div class="sourceCode" id="cb13"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>&lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkDialog&quot;</span><span class="ot"> id=</span><span class="st">&quot;pref&quot;</span>&gt;</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;Preferences&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;resizable&quot;</span>&gt;FALSE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;modal&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;transient-for&quot;</span>&gt;win&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">child</span><span class="ot"> internal-child=</span><span class="st">&quot;content_area&quot;</span>&gt;</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;content_area&quot;</span>&gt;</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>      &lt;<span class="kw">child</span>&gt;</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;pref_boxh&quot;</span>&gt;</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_HORIZONTAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;spacing&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-start&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-end&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-top&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-bottom&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">child</span>&gt;</span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;fontlabel&quot;</span>&gt;</span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a>              &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Font:&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a>              &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;xalign&quot;</span>&gt;1&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a>            &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a>          &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">child</span>&gt;</span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkFontButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;fontbtn&quot;</span>&gt;</span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true" tabindex="-1"></a>            &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true" tabindex="-1"></a>          &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a>        &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true" tabindex="-1"></a>      &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a>  &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true" tabindex="-1"></a>&lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<ul>
<li>Preference dialog is an independent dialog. It is not a descendant
widget of the top-level GtkApplicationwindow. Therefore, There’s no
child tag that surrounds the dialog object.</li>
<li>There are four properties of the dialog. GtkDialog is a child object
(not child widget) of GtkWindow, so it inherits all the properties from
GtkWindow. Title, resizable, modal and transient-for properties are
inherited from GtkWindow. Transient-for specifies a temporary parent
window, which the dialog’s location is based on.</li>
<li>The tag <code>&lt;child internal-child="content_area"&gt;</code> is
put at the top of the contents of the dialog. You need to specify a
GtkBox object tag with content_area id. This object is defined in
<code>gtkdialog.ui</code> (composite widget) but you need to define it
again in the child tag. Composite widget will be explained in the next
section. For further information about GtkDialog ui tags, see:
<ul>
<li><a
href="https://docs.gtk.org/gtk4/class.Builder.html#gtkbuilder-ui-definitions">GTK
4 API reference – GtkBuilder</a></li>
<li><a
href="https://docs.gtk.org/gtk4/class.Dialog.html#gtkdialog-as-gtkbuildable">GTK
4 API reference – GtkDialog</a></li>
<li><a
href="https://gitlab.gnome.org/GNOME/gtk/-/blob/main/gtk/ui/gtkdialog.ui">GtkDialog
ui file</a></li>
</ul></li>
<li>There is a horizontal GtkBox in the content area.</li>
<li>GtkLabel and GtkFontButton are in the GtkBox.</li>
</ul>
<p>I want the preference dialog to keep alive during the application
lives. So, it is necessary to catch “close-request” signal from the
dialog and stop the signal propagation. (This signal is emitted when the
close button, right upper x button of the window, is clicked.) This is
accomplished by returning TRUE by the signal handler.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>dialog_close_cb <span class="op">(</span>GtkDialog <span class="op">*</span>dialog<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>  gtk_widget_set_visible <span class="op">(</span>GTK_WIDGET <span class="op">(</span>dialog<span class="op">),</span> false<span class="op">);</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> TRUE<span class="op">;</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="op">(</span> in app_startup function <span class="op">)</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>pref_close_request_handler_id <span class="op">=</span> g_signal_connect <span class="op">(</span>GTK_DIALOG <span class="op">(</span>pref<span class="op">),</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>dialog_close_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span></code></pre></div>
<p>Generally, signal emission consists of five stages.</p>
<ol type="1">
<li>Default handler is invoked if the signal’s flag is
<code>G_SIGNAL_RUN_FIRST</code>. Default handler is set when a signal is
registered. It is different from user signal handler, simply called
signal handler, connected by <code>g_signal_connect</code>series
function. Default handler can be invoked in either stage 1, 3 or 5. Most
of the default handlers are <code>G_SIGNAL_RUN_FIRST</code> or
<code>G_SIGNAL_RUN_LAST</code>.</li>
<li>Signal handlers are invoked, unless it is connected by
<code>g_signal_connect_after</code>.</li>
<li>Default handler is invoked if the signal’s flag is
<code>G_SIGNAL_RUN_LAST</code>.</li>
<li>Signal handlers are invoked, if it is connected by
<code>g_signal_connect_after</code>.</li>
<li>Default handler is invoked if the signal’s flag is
<code>G_SIGNAL_RUN_CLEANUP</code>.</li>
</ol>
<p>The “close-request” signal is <code>G_SIGNAL_RUN_LAST</code>. So, the
order of the invocation is:</p>
<ol type="1">
<li>Signal handler <code>dialog_close_cb</code></li>
<li>Default handler</li>
</ol>
<p>And If the user signal handler returns TRUE, then other handlers will
be stopped being invoked. Therefore, the program above prevents the
invocation of the default handler and stop the closing process of the
dialog.</p>
<p>The following codes are extracted from
<code>tfeapplication.c</code>.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong pref_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gulong alert_close_request_handler_id <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>dialog_close_cb <span class="op">(</span>GtkDialog <span class="op">*</span>dialog<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>  gtk_widget_set_visible <span class="op">(</span>GTK_WIDGET <span class="op">(</span>dialog<span class="op">),</span> false<span class="op">);</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> TRUE<span class="op">;</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a>pref_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a>  gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>pref<span class="op">));</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a>   <span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>pref_close_request_handler_id <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a>    g_signal_handler_disconnect <span class="op">(</span>pref<span class="op">,</span> pref_close_request_handler_id<span class="op">);</span></span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a>  gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>pref<span class="op">));</span></span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a>   <span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb15-23"><a href="#cb15-23" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-24"><a href="#cb15-24" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb15-25"><a href="#cb15-25" aria-hidden="true" tabindex="-1"></a>tfe_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-26"><a href="#cb15-26" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb15-27"><a href="#cb15-27" aria-hidden="true" tabindex="-1"></a>  pref <span class="op">=</span> GTK_DIALOG <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;pref&quot;</span><span class="op">));</span></span>
<span id="cb15-28"><a href="#cb15-28" aria-hidden="true" tabindex="-1"></a>  pref_close_request_handler_id <span class="op">=</span> g_signal_connect <span class="op">(</span>GTK_DIALOG <span class="op">(</span>pref<span class="op">),</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>dialog_close_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb15-29"><a href="#cb15-29" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span> </span>
<span id="cb15-30"><a href="#cb15-30" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>The close-requiest signal on the preference dialog is connected to
the handler <code>dialog_close_cb</code>. It changes the close behavior
of the dialog. When the signal is emitted, the visibility is set to
false and the default handler is canceled. So, the dialog just
disappears but exists.</li>
<li>A handler <code>pref_activate</code> shows the preference
dialog.</li>
<li>The shutdown handler <code>app_shutdown</code> disconnects the
handlers from the “close-request”signal and destroys <code>pref</code>
window.</li>
</ul>
<h3 id="alert-dialog">Alert dialog</h3>
<p>If a user closes a page without saving, it is advisable to show an
alert for a user to confirm it. Alert dialog is used in such a
situation.</p>
<div class="sourceCode" id="cb16"><pre
class="sourceCode xml"><code class="sourceCode xml"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>  &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkDialog&quot;</span><span class="ot"> id=</span><span class="st">&quot;alert&quot;</span>&gt;</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;Are you sure?&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;resizable&quot;</span>&gt;FALSE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;modal&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;transient-for&quot;</span>&gt;win&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">child</span><span class="ot"> internal-child=</span><span class="st">&quot;content_area&quot;</span>&gt;</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>      &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span>&gt;</span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">child</span>&gt;</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>          &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span>&gt;</span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_HORIZONTAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;spacing&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-start&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-end&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-top&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;margin-bottom&quot;</span>&gt;12&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">child</span>&gt;</span>
<span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a>              &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkImage&quot;</span>&gt;</span>
<span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a>                &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;icon-name&quot;</span>&gt;dialog-warning&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-19"><a href="#cb16-19" aria-hidden="true" tabindex="-1"></a>                &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;icon-size&quot;</span>&gt;GTK_ICON_SIZE_LARGE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-20"><a href="#cb16-20" aria-hidden="true" tabindex="-1"></a>              &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb16-21"><a href="#cb16-21" aria-hidden="true" tabindex="-1"></a>            &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb16-22"><a href="#cb16-22" aria-hidden="true" tabindex="-1"></a>            &lt;<span class="kw">child</span>&gt;</span>
<span id="cb16-23"><a href="#cb16-23" aria-hidden="true" tabindex="-1"></a>              &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;lb_alert&quot;</span>&gt;</span>
<span id="cb16-24"><a href="#cb16-24" aria-hidden="true" tabindex="-1"></a>              &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb16-25"><a href="#cb16-25" aria-hidden="true" tabindex="-1"></a>            &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb16-26"><a href="#cb16-26" aria-hidden="true" tabindex="-1"></a>          &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb16-27"><a href="#cb16-27" aria-hidden="true" tabindex="-1"></a>        &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb16-28"><a href="#cb16-28" aria-hidden="true" tabindex="-1"></a>      &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb16-29"><a href="#cb16-29" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb16-30"><a href="#cb16-30" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">child</span><span class="ot"> type=</span><span class="st">&quot;action&quot;</span>&gt;</span>
<span id="cb16-31"><a href="#cb16-31" aria-hidden="true" tabindex="-1"></a>      &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btn_cancel&quot;</span>&gt;</span>
<span id="cb16-32"><a href="#cb16-32" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Cancel&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-33"><a href="#cb16-33" aria-hidden="true" tabindex="-1"></a>      &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb16-34"><a href="#cb16-34" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb16-35"><a href="#cb16-35" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">child</span><span class="ot"> type=</span><span class="st">&quot;action&quot;</span>&gt;</span>
<span id="cb16-36"><a href="#cb16-36" aria-hidden="true" tabindex="-1"></a>      &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btn_accept&quot;</span>&gt;</span>
<span id="cb16-37"><a href="#cb16-37" aria-hidden="true" tabindex="-1"></a>        &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Close&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb16-38"><a href="#cb16-38" aria-hidden="true" tabindex="-1"></a>      &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb16-39"><a href="#cb16-39" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb16-40"><a href="#cb16-40" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">action-widgets</span>&gt;</span>
<span id="cb16-41"><a href="#cb16-41" aria-hidden="true" tabindex="-1"></a>      &lt;<span class="kw">action-widget</span><span class="ot"> response=</span><span class="st">&quot;cancel&quot;</span><span class="ot"> default=</span><span class="st">&quot;true&quot;</span>&gt;btn_cancel&lt;/<span class="kw">action-widget</span>&gt;</span>
<span id="cb16-42"><a href="#cb16-42" aria-hidden="true" tabindex="-1"></a>      &lt;<span class="kw">action-widget</span><span class="ot"> response=</span><span class="st">&quot;accept&quot;</span>&gt;btn_accept&lt;/<span class="kw">action-widget</span>&gt;</span>
<span id="cb16-43"><a href="#cb16-43" aria-hidden="true" tabindex="-1"></a>    &lt;/<span class="kw">action-widgets</span>&gt;</span>
<span id="cb16-44"><a href="#cb16-44" aria-hidden="true" tabindex="-1"></a>    &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;response&quot;</span><span class="ot"> handler=</span><span class="st">&quot;alert_response_cb&quot;</span><span class="ot"> swapped=</span><span class="st">&quot;NO&quot;</span><span class="ot"> object=</span><span class="st">&quot;nb&quot;</span>&gt;&lt;/<span class="kw">signal</span>&gt;</span>
<span id="cb16-45"><a href="#cb16-45" aria-hidden="true" tabindex="-1"></a>  &lt;/<span class="kw">object</span>&gt;</span></code></pre></div>
<p>This ui file describes the alert dialog. Some part are the same as
preference dialog. There are two objects in the content area, GtkImage
and GtkLabel.</p>
<p>GtkImage shows an image. The image can comes from files, resources,
icon theme and so on. The image above displays an icon from the current
icon theme. You can see icons in the theme by
<code>gtk4-icon-browser</code>.</p>
<pre><code>$ gtk4-icon-browser</code></pre>
<p>The “dialog-warning” icon is something like this.</p>
<figure>
<img src="image/dialog_warning.png"
alt="dialog-warning icon is like …" />
<figcaption aria-hidden="true">dialog-warning icon is like
…</figcaption>
</figure>
<p>These are made by my hand. The real image on the alert dialog is
nicer.</p>
<p>The GtkLabel <code>lb_alert</code> has no text yet. An alert message
will be inserted in the program.</p>
<p>There are two child tags which have “action” type. They are button
objects located in the action area. Action-widgets tag describes the
actions of the buttons. The button <code>btn_cancel</code> emits
response signal with cancel response (<code>GTK_RESPONSE_CANCEL</code>)
if it is clicked on. The button <code>btn_accept</code> emits response
signal with accept response (<code>GTK_RESPONSE_ACCEPT</code>) if it is
clicked on. The response signal is connected to
<code>alert_response_cb</code> handler.</p>
<p>The alert dialog keeps alive while the application lives. The
“close-request” signal is stopped by the handler
<code>dialog_close_cb</code> like the preference dialog.</p>
<h3 id="alert-dialog-and-close-handlers">Alert dialog and close
handlers</h3>
<p>If a user closes a page or quits the application without saving the
contents, the alert dialog appears. There are four handlers, close_cb,
close_activated, win_close_request_cb and close_all_activated. The first
two are called when a notebook page is closed. The others are called
when the main window is closed — so, all the notebooks are closed.</p>
<ul>
<li>close button =&gt; close_cb (=&gt; alert dialog)</li>
<li>Ctrl-W =&gt; close_activated =&gt; close_cb (=&gt; alert
dialog)</li>
<li>Close button (x button at the right top of the main window) =&gt;
win_close_request_cb (=&gt; alert dialog)</li>
<li>Quit menu or Ctrl-Q =&gt; close_all_activated =&gt;
win_close_request_cb (=&gt; alert dialog)</li>
</ul>
<div class="sourceCode" id="cb18"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean is_quit<span class="op">;</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> gboolean</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>win_close_request_cb <span class="op">(</span>GtkWindow <span class="op">*</span>win<span class="op">,</span> GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>  is_quit <span class="op">=</span> true<span class="op">;</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>has_saved_all <span class="op">(</span>nb<span class="op">))</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> false<span class="op">;</span></span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a>    gtk_label_set_text <span class="op">(</span>lb_alert<span class="op">,</span> <span class="st">&quot;Contents aren&#39;t saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to quit?&quot;</span><span class="op">);</span></span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a>    gtk_button_set_label <span class="op">(</span>btn_accept<span class="op">,</span> <span class="st">&quot;Quit&quot;</span><span class="op">);</span></span>
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a>    gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>alert<span class="op">));</span></span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> true<span class="op">;</span></span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a>close_cb <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-18"><a href="#cb18-18" aria-hidden="true" tabindex="-1"></a>  is_quit <span class="op">=</span> false<span class="op">;</span></span>
<span id="cb18-19"><a href="#cb18-19" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>has_saved <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">)))</span></span>
<span id="cb18-20"><a href="#cb18-20" aria-hidden="true" tabindex="-1"></a>    notebook_page_close <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">));</span></span>
<span id="cb18-21"><a href="#cb18-21" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb18-22"><a href="#cb18-22" aria-hidden="true" tabindex="-1"></a>    gtk_label_set_text <span class="op">(</span>lb_alert<span class="op">,</span> <span class="st">&quot;Contents aren&#39;t saved yet.</span><span class="sc">\n</span><span class="st">Are you sure to close?&quot;</span><span class="op">);</span></span>
<span id="cb18-23"><a href="#cb18-23" aria-hidden="true" tabindex="-1"></a>    gtk_button_set_label <span class="op">(</span>btn_accept<span class="op">,</span> <span class="st">&quot;Close&quot;</span><span class="op">);</span></span>
<span id="cb18-24"><a href="#cb18-24" aria-hidden="true" tabindex="-1"></a>    gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>alert<span class="op">));</span></span>
<span id="cb18-25"><a href="#cb18-25" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb18-26"><a href="#cb18-26" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb18-27"><a href="#cb18-27" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-28"><a href="#cb18-28" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb18-29"><a href="#cb18-29" aria-hidden="true" tabindex="-1"></a>close_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-30"><a href="#cb18-30" aria-hidden="true" tabindex="-1"></a>  GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb18-31"><a href="#cb18-31" aria-hidden="true" tabindex="-1"></a>  close_cb <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb18-32"><a href="#cb18-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb18-33"><a href="#cb18-33" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-34"><a href="#cb18-34" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb18-35"><a href="#cb18-35" aria-hidden="true" tabindex="-1"></a>close_all_activated <span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-36"><a href="#cb18-36" aria-hidden="true" tabindex="-1"></a>  GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb18-37"><a href="#cb18-37" aria-hidden="true" tabindex="-1"></a>  GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>nb<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb18-38"><a href="#cb18-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-39"><a href="#cb18-39" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span> win_close_request_cb <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> nb<span class="op">))</span> <span class="co">// checks whether contents are saved</span></span>
<span id="cb18-40"><a href="#cb18-40" aria-hidden="true" tabindex="-1"></a>    gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb18-41"><a href="#cb18-41" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb18-42"><a href="#cb18-42" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-43"><a href="#cb18-43" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb18-44"><a href="#cb18-44" aria-hidden="true" tabindex="-1"></a>alert_response_cb <span class="op">(</span>GtkDialog <span class="op">*</span>alert<span class="op">,</span> <span class="dt">int</span> response_id<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-45"><a href="#cb18-45" aria-hidden="true" tabindex="-1"></a>  GtkNotebook <span class="op">*</span>nb <span class="op">=</span> GTK_NOTEBOOK <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb18-46"><a href="#cb18-46" aria-hidden="true" tabindex="-1"></a>  GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_widget_get_ancestor <span class="op">(</span>GTK_WIDGET <span class="op">(</span>nb<span class="op">),</span> GTK_TYPE_WINDOW<span class="op">);</span></span>
<span id="cb18-47"><a href="#cb18-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-48"><a href="#cb18-48" aria-hidden="true" tabindex="-1"></a>  gtk_widget_set_visible <span class="op">(</span>GTK_WIDGET <span class="op">(</span>alert<span class="op">),</span> false<span class="op">);</span></span>
<span id="cb18-49"><a href="#cb18-49" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>response_id <span class="op">==</span> GTK_RESPONSE_ACCEPT<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-50"><a href="#cb18-50" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>is_quit<span class="op">)</span></span>
<span id="cb18-51"><a href="#cb18-51" aria-hidden="true" tabindex="-1"></a>      gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb18-52"><a href="#cb18-52" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span></span>
<span id="cb18-53"><a href="#cb18-53" aria-hidden="true" tabindex="-1"></a>      notebook_page_close <span class="op">(</span>nb<span class="op">);</span></span>
<span id="cb18-54"><a href="#cb18-54" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb18-55"><a href="#cb18-55" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb18-56"><a href="#cb18-56" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-57"><a href="#cb18-57" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb18-58"><a href="#cb18-58" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb18-59"><a href="#cb18-59" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-60"><a href="#cb18-60" aria-hidden="true" tabindex="-1"></a>  build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/tfe/tfe.ui&quot;</span><span class="op">);</span></span>
<span id="cb18-61"><a href="#cb18-61" aria-hidden="true" tabindex="-1"></a>  win <span class="op">=</span> GTK_APPLICATION_WINDOW <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb18-62"><a href="#cb18-62" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-63"><a href="#cb18-63" aria-hidden="true" tabindex="-1"></a>  g_signal_connect <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;close-request&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>win_close_request_cb<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb18-64"><a href="#cb18-64" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb18-65"><a href="#cb18-65" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The static variable <code>is_quit</code> is true when user tries to
quit the application and false otherwise.</p>
<ul>
<li>When a user clicks on the close button, <code>close_cb</code>
handler is invoked. The handler sets <code>is_quit</code> to false. The
function <code>has_saved</code> returns true if the current page has
been saved. If it is true, it calls <code>notebook_page_close</code> to
close the current page. Otherwise, it shows the alert dialog. The
response signal of the dialog is connected to the handler
<code>alert_response_cb</code>. It hides the dialog first. Then checks
the <code>response_id</code>. If it is <code>GTK_RESPONSE_ACCEPT</code>,
which means the user has clicked on the close button, then it closes the
current page. Otherwise it does nothing.</li>
<li>When a user presses “Ctrl-w”, <code>close_activated</code> handler
is invoked. It just calls <code>close_cb</code>.</li>
<li>When a user clicks the close button of the main window,
“close-request” signal is emitted on the window. The signal has been
connected to the <code>win_close_request_cb</code> handler in advance.
The connection is done in the start up handler on the application. The
<code>win_close_request_cb</code> handler sets <code>is_quit</code> to
be true. If <code>has_save_all</code> returns true, it returns false,
which means the signal moves to the default handler and the main window
will close. Otherwise, It shows the alert dialog and returns true. So,
the signal stops and the default handler won’t be called. But if the
user clicked accept button in the alert dialog, the response handler
<code>alert_response_cb</code> calls <code>gtk_window_destroy</code> and
the main window will be closed.</li>
<li>When a user clicked on the quit menu or presses “Ctrl-q”, then
<code>close_all_activated</code> handler is invoked. It calls
<code>win_close_request_cb</code>. If the return value is false, it
destroys the main window. Otherwise it does nothing, but
<code>win_close_request_cb</code> has shown the alert dialog.</li>
</ul>
<h3 id="has_saved-and-has_saved_all-functions">Has_saved and
has_saved_all functions</h3>
<p>The two functions are defined in the file <code>tfenotebook.c</code>.
They are public functions.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/tfenotebook.c has_saved has_saved_all @@@</p>
<ul>
<li>1-14: <code>has_saved</code> function.</li>
<li>10: The function <code>gtk_text_buffer_get_modified</code> returns
true if the content of the buffer has been modified since the modified
flag had set false. The flag is set to false when:
<ul>
<li>the buffer is created.</li>
<li>the contents of the buffer is replaced</li>
<li>the contents of the buffer is saved to a file.</li>
</ul></li>
<li>10-13: This function returns true if the contents of the current
page has been saved and no modification has been made. It returns false,
if the current page has been modified and hasn’t been saved.</li>
<li>16-34: <code>has_saved_all</code> function. This function is similar
to <code>has_saved</code> function. It returns true if all the pages
have been saved. It returns false if at least one page has been modified
since it last had been saved.</li>
</ul>
<h2 id="notebook-page-tab">Notebook page tab</h2>
<p>If you have some pages and edit them together, you might be confused
which file needs to be saved. Common file editors changes the tab when
the contents are modified. GtkTextBuffer provides “modified-changed”
signal to notify the modification.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>notebook_page_build <span class="op">(</span>GtkNotebook <span class="op">*</span>nb<span class="op">,</span> GtkWidget <span class="op">*</span>tv<span class="op">,</span> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">)</span> <span class="op">{</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>  g_signal_connect <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> <span class="st">&quot;change-file&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>file_changed_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>  g_signal_connect <span class="op">(</span>tb<span class="op">,</span> <span class="st">&quot;modified-changed&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>modified_changed_cb<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>When a page is built, connect “change-file” and “modified-changed”
signals to <code>file_changed_cb</code> and
<code>modified_changed_cb</code> handlers respectively.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/tfenotebook.c file_changed_cb modified_changed_cb @@@</p>
<p>The <code>file_changed_cb</code> handler gives a new file name to the
notebook page tag. The <code>modified_changed_cb</code> handler inserts
an asterisk at the beginning of the filename. It is a sign that
indicates the file has been modified but not saved yet.</p>
<ul>
<li>1-20: <code>file_changed_cb</code> handler.</li>
<li>9-10: If the signal emits during the page is being built, it is
possible that <code>tv</code> isn’t a descendant of <code>nb</code>.
That is to say, there’s no page corresponds to <code>tv</code>. Then, it
is unnecessary to change the name of the tab because no tab exists.</li>
<li>13-15: If <code>file</code> is GFile, then it gets the filename and
release the reference to <code>file</code>.</li>
<li>16-17: Otherwise, it assigns “Untitled” (+ a number) to
<code>filename</code></li>
<li>18-19: Creates GtkLabel with <code>filename</code> and sets the tab
of the page with the GtkLabel.</li>
<li>22-41: <code>modified_changed_cb</code> handler.</li>
<li>31-32: If <code>tv</code> isn’t a descendant of <code>nb</code>,
then nothing needs to be done.</li>
<li>33-35: If the content is modified, then it gets the text of the tab
and adds asterisk at the beginning of the text.</li>
<li>36-38: Sets the tab with the filename with the asterisk</li>
<li>39-40: Otherwise it calls <code>file_changed_cb</code> and updates
the filename (without an asterisk).</li>
</ul>
<h2 id="font">Font</h2>
<h3 id="gtkfontbutton-and-gtkfontchooser">GtkFontButton and
GtkFontChooser</h3>
<p>GtkFontButton is a button class which displays the current font and a
user can change the font with the button. It opens a font chooser dialog
if a user clicked on the button. A user can change the font (family,
style, weight and size) with the dialog. Then the button keeps the new
font and displays it.</p>
<p>The button is set with a builder in the application startup process.
And the signal “font-set” is connected to the handler
<code>font_set_cb</code>. The signal “font-set” is emitted when the user
selects a font.</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>font_set_cb <span class="op">(</span>GtkFontButton <span class="op">*</span>fontbtn<span class="op">)</span> <span class="op">{</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  PangoFontDescription <span class="op">*</span>pango_font_desc<span class="op">;</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">char</span> <span class="op">*</span>s<span class="op">,</span> <span class="op">*</span>css<span class="op">;</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>  pango_font_desc <span class="op">=</span> gtk_font_chooser_get_font_desc <span class="op">(</span>GTK_FONT_CHOOSER <span class="op">(</span>fontbtn<span class="op">));</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>  s <span class="op">=</span> pfd2css <span class="op">(</span>pango_font_desc<span class="op">);</span> <span class="co">// converts Pango Font Description into CSS style string</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>  css <span class="op">=</span> g_strdup_printf <span class="op">(</span><span class="st">&quot;textview {</span><span class="sc">%s</span><span class="st">}&quot;</span><span class="op">,</span> s<span class="op">);</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>  gtk_css_provider_load_from_data <span class="op">(</span>provider<span class="op">,</span> css<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a>  g_free <span class="op">(</span>s<span class="op">);</span></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a>  g_free <span class="op">(</span>css<span class="op">);</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb20-15"><a href="#cb20-15" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb20-16"><a href="#cb20-16" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb20-17"><a href="#cb20-17" aria-hidden="true" tabindex="-1"></a>  fontbtn <span class="op">=</span> GTK_FONT_BUTTON <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;fontbtn&quot;</span><span class="op">));</span></span>
<span id="cb20-18"><a href="#cb20-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb20-19"><a href="#cb20-19" aria-hidden="true" tabindex="-1"></a>  g_signal_connect <span class="op">(</span>fontbtn<span class="op">,</span> <span class="st">&quot;font-set&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>font_set_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb20-20"><a href="#cb20-20" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb20-21"><a href="#cb20-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>GtkFontChooser is an interface implemented by GtkFontButton. The
function <code>gtk_font_chooser_get_font_desc</code> gets the
PangoFontDescription of the currently selected font.
PangoFontDescription includes font family, style, weight and size in it.
The function <code>pfd2css</code> converts them to CSS style string. The
following shows the conversion.</p>
<pre><code>PangoFontDescription:
  font-family: Monospace
  font-style: normal
  font-weight: normal
  font-size: 12pt
=&gt;
&quot;font-family: Monospace; font-style: normal; font-weight: 400; font-size: 12pt;&quot;</code></pre>
<p>Then, <code>font_set_cb</code> creates a CSS string and put it into
the <code>provider</code> instance. The provider has been added to the
default display in advance. So, the handler effects the font for the
contents of the textview immediately.</p>
<h3 id="css-and-pango">CSS and Pango</h3>
<p>Convertors from PangoFontDescription to CSS are packed in
<code>pfd2css.c</code>. The filename means:</p>
<ul>
<li>pfd =&gt; PangoFontDescripter</li>
<li>2 =&gt; to</li>
<li>css =&gt; CSS (Cascade Style Sheet)</li>
</ul>
<p>All the public functions in the file have “pdf2css” prefix.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/pfd2css.c @@@</p>
<ul>
<li>1: Public functions, constants and structures for Pango is defined
in <code>pango/pango.h</code>.</li>
<li>2: Including <code>pdf2css.h</code> makes it possible to call public
functions anywhere in the file <code>pdf2css.c</code>. Because the
header file includes declarations of all the public functions.</li>
<li>7-16: <code>pdf2css</code> function. This function gets font family,
style, weight and size from a PangoFontDescription instance given as an
argument. And it builds them to a string. The returned string is owned
by caller. The caller should free the string when it is useless.</li>
<li>20-23: <code>pfd2css_famili</code> function. This function gets
font-family string from a PangoFontDescription instance. The string is
owned by the PFD instance so caller can’t modify or free the
string.</li>
<li>25-38: <code>pdf2css_style</code> function. This function gets
font-style string from a PangoFontDescription instance. The string is
static and caller can’t modify or free it.</li>
<li>40-71: <code>pfd2css_weight</code> function. This function gets
font-weight integer value from a PangoFontDescription instance. The
value is in between 100 to 900. It is defined in CSS Fonts Module Level
3 specification.
<ul>
<li>100 - Thin</li>
<li>200 - Extra Light (Ultra Light)</li>
<li>300 - Light</li>
<li>400 - Normal</li>
<li>500 - Medium</li>
<li>600 - Semi Bold (Demi Bold)</li>
<li>700 - Bold</li>
<li>800 - Extra Bold (Ultra Bold)</li>
<li>900 - Black (Heavy)</li>
</ul></li>
<li>73-79: <code>pdf2css_size</code> function. This function gets
font-size string from a PangoFontDescription instance. The string is
owned by caller, so the caller should free it when it is useless.
PangoFontDescription has absolute or non-absolute size.
<ul>
<li>If it is absolute, the size is in device units.</li>
<li>If it is non-absolute, the size is in points.</li>
</ul></li>
<li>The definition of device units is dependent on the output device. It
will typically be pixels for a screen, and points for a printer.</li>
<li>Pango holds the size as its own dimensions. The constant
<code>PANGO_SCALE</code> is the scale between dimensions used for Pango
distances and device units. <code>PANGO_SCALE</code> is currently 1024,
but this may be changed in the future. When setting font sizes, device
units are always considered to be points rather than pixels. If the font
size is 12pt, the size in pango is
<code>12*PANGO_SCALE=12*1024=12288</code>.</li>
</ul>
<p>For further information, see <a
href="https://docs.gtk.org/Pango/">Pango API Reference</a>.</p>
<h2 id="gsettings">GSettings</h2>
<p>We want to maintain the font data after the application quits. There
are some ways to implement it.</p>
<ul>
<li>Make a configuration file. For example, a text file
“~/.config/tfe/font.cfg” keeps font information.</li>
<li>Use GSettings object. The basic idea of GSettings are similar to
configuration file. Configuration information data is put into a
database file.</li>
</ul>
<p>GSettings is simple and easy to use but a bit hard to understand the
concept. This subsection describes the concept first and then how to
program it.</p>
<h3 id="gsettings-schema">GSettings schema</h3>
<p>GSettings schema describes a set of keys, value types and some other
information. GSettings object uses this schema and it writes/reads the
value of a key to/from the right place in the database.</p>
<ul>
<li>A schema has an id. The id must be unique. We often use the same
string as application id, but schema id and application id are
different. You can use different name from application id. Schema id is
a string delimited by periods. For example, “com.github.ToshioCP.tfe” is
a correct schema id.</li>
<li>A schema usually has a path. The path is a location in the database.
Each key is stored under the path. For example, if a key
<code>font</code> is defined with a path
<code>/com/github/ToshioCP/tfe/</code>, the key’s location in the
database is <code>/com/github/ToshioCP/tfe/font</code>. Path is a string
begins with and ends with a slash (<code>/</code>). And it is delimited
by slashes.</li>
<li>GSettings save information as key-value style. Key is a string
begins with lower case characters followed by lower case, digit or dash
(<code>-</code>) and ends with lower case or digit. No consecutive
dashes are allowed. Values can be any type. GSettings stores values as
GVariant type, which can be, for example, integer, double, boolean,
string or complex types like an array. The type of values needs to be
defined in the schema.</li>
<li>A default value needs to be set for each key.</li>
<li>A summery and description can be set for each key optionally.</li>
</ul>
<p>Schemas are described in an XML format. For example,</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/com.github.ToshioCP.tfe.gschema.xml @@@</p>
<ul>
<li>4: The type attribute is “s”. It is GVariant type string. For
GVariant type string, see <a
href="https://docs.gtk.org/glib/struct.VariantType.html#gvariant-type-strings">GLib
API Reference – GVariant Type Strings</a>. Other common types are:
<ul>
<li>“b”: gboolean</li>
<li>“i”: gint32.</li>
<li>“d”: double.</li>
</ul></li>
</ul>
<p>Further information is in:</p>
<ul>
<li><a
href="https://docs.gtk.org/glib/gvariant-format-strings.html">GLib API
Reference – GVariant Format Strings</a></li>
<li><a href="https://docs.gtk.org/glib/gvariant-text.html">GLib API
Reference – GVariant Text Format</a></li>
<li><a href="https://docs.gtk.org/glib/struct.Variant.html">GLib API
Reference – GVariant</a></li>
<li><a href="https://docs.gtk.org/glib/struct.VariantType.html">GLib API
Reference – VariantType</a></li>
</ul>
<h3 id="gsettings-1">gsettings</h3>
<p>First, let’s try <code>gsettings</code> application. It is a
configuration tool for GSettings.</p>
<pre><code>$ gsettings help
Usage:
  gsettings --version
  gsettings [--schemadir SCHEMADIR] COMMAND [ARGS?]

Commands:
  help                      Show this information
  list-schemas              List installed schemas
  list-relocatable-schemas  List relocatable schemas
  list-keys                 List keys in a schema
  list-children             List children of a schema
  list-recursively          List keys and values, recursively
  range                     Queries the range of a key
  describe                  Queries the description of a key
  get                       Get the value of a key
  set                       Set the value of a key
  reset                     Reset the value of a key
  reset-recursively         Reset all values in a given schema
  writable                  Check if a key is writable
  monitor                   Watch for changes

Use &quot;gsettings help COMMAND&quot; to get detailed help.</code></pre>
<p>List schemas.</p>
<pre><code>$ gsettings list-schemas
org.gnome.rhythmbox.podcast
ca.desrt.dconf-editor.Demo.Empty
org.gnome.gedit.preferences.ui
org.gnome.evolution-data-server.calendar
org.gnome.rhythmbox.plugins.generic-player

... ...
</code></pre>
<p>Each line is an id of a schema. Each schema has a key-value
configuration data. You can see them with list-recursively command.
Let’s look at the keys and values of <code>org.gnome.calculator</code>
schema.</p>
<pre><code>$ gsettings list-recursively org.gnome.calculator
org.gnome.calculator source-currency &#39;&#39;
org.gnome.calculator source-units &#39;degree&#39;
org.gnome.calculator button-mode &#39;basic&#39;
org.gnome.calculator target-currency &#39;&#39;
org.gnome.calculator base 10
org.gnome.calculator angle-units &#39;degrees&#39;
org.gnome.calculator word-size 64
org.gnome.calculator accuracy 9
org.gnome.calculator show-thousands false
org.gnome.calculator window-position (122, 77)
org.gnome.calculator refresh-interval 604800
org.gnome.calculator target-units &#39;radian&#39;
org.gnome.calculator precision 2000
org.gnome.calculator number-format &#39;automatic&#39;
org.gnome.calculator show-zeroes false</code></pre>
<p>This schema is used by GNOME Calculator. Run the calculator and
change the mode, then check the schema again.</p>
<pre><code>$ gnome-calculator</code></pre>
<figure>
<img src="image/gnome_calculator_basic.png"
alt="gnome-calculator basic mode" />
<figcaption aria-hidden="true">gnome-calculator basic mode</figcaption>
</figure>
<p>Change the mode to advanced and quit.</p>
<figure>
<img src="image/gnome_calculator_advanced.png"
alt="gnome-calculator advanced mode" />
<figcaption aria-hidden="true">gnome-calculator advanced
mode</figcaption>
</figure>
<p>Run gsettings and check the value of <code>button-mode</code>.</p>
<pre><code>$ gsettings list-recursively org.gnome.calculator

... ...

org.gnome.calculator button-mode &#39;advanced&#39;

... ...
</code></pre>
<p>Now we know that GNOME Calculator used gsettings and it has set
<code>button-mode</code> key to “advanced”. The value remains even the
calculator quits. So when the calculator runs again, it will appear as
an advanced mode.</p>
<h3 id="glib-compile-schemas">glib-compile-schemas</h3>
<p>GSettings schemas are specified with an XML format. The XML schema
files must have the filename extension <code>.gschema.xml</code>. The
following is the XML schema file for the application
<code>tfe</code>.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/com.github.ToshioCP.tfe.gschema.xml @@@</p>
<p>The filename is “com.github.ToshioCP.tfe.gschema.xml”. Schema XML
filenames are usually the schema id followed by “.gschema.xml” suffix.
You can use different name from schema id, but it is not
recommended.</p>
<ul>
<li>2: The top level element is <code>&lt;schemalist&gt;</code>.</li>
<li>3: schema tag has <code>path</code> and <code>id</code> attributes.
A path determines where the settings are stored in the conceptual global
tree of settings. An id identifies the schema.</li>
<li>4: Key tag has two attributes. Name is the name of the key. Type is
the type of the value of the key and it is a GVariant Format
String.</li>
<li>5: default value of the key <code>font</code> is
<code>Monospace 12</code>.</li>
<li>6: Summery and description elements describes the key. They are
optional, but it is recommended to add them in the XML file.</li>
</ul>
<p>The XML file is compiled by glib-compile-schemas. When compiling,
<code>glib-compile-schemas</code> compiles all the XML files which have
“.gschema.xml” file extension in the directory given as an argument. It
converts the XML file into a binary file <code>gschemas.compiled</code>.
Suppose the XML file above is under <code>tfe6</code> directory.</p>
<pre><code>$ glib-compile-schemas tfe6</code></pre>
<p>Then, <code>gschemas.compiled</code> is generated under
<code>tfe6</code>. When you test your application, set
<code>GSETTINGS_SCHEMA_DIR</code> environment variable so that GSettings
objet can find <code>gschemas.compiled</code>.</p>
<pre><code>$ GSETTINGS_SCHEMA_DIR=(the directory gschemas.compiled is located):$GSETTINGS_SCHEMA_DIR (your application name)</code></pre>
<p>GSettings object looks for this file by the following process.</p>
<ul>
<li>It searches <code>glib-2.0/schemas</code> subdirectories of all the
directories specified in the environment variable
<code>XDG_DATA_DIRS</code>. Common directores are
<code>/usr/share/glib-2.0/schemas</code> and
`<code>/usr/local/share/glib-2.0/schemas</code>.</li>
<li>If <code>GSETTINGS_SCHEMA_DIR</code> environment variable is
defined, it searches all the directories specified in the variable.
<code>GSETTINGS_SCHEMA_DIR</code> can specify multiple directories
delimited by colon (:).</li>
</ul>
<p>In the directories above, all the <code>.gschema.xml</code> files are
stored. Therefore, when you install your application, follow the
instruction below to install your schemas.</p>
<ol type="1">
<li>Make <code>.gschema.xml</code> file.</li>
<li>Copy it to one of the directories above. For example,
<code>/usr/local/share/glib-2.0/schemas</code>.</li>
<li>Run <code>glib-compile-schemas</code> on the directory above. You
maybe need <code>sudo</code>.</li>
</ol>
<h3 id="gsettings-object-and-g_settings_bind">GSettings object and
g_settings_bind</h3>
<p>Now, we go on to the next topic — how to program GSettings.</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> GSettings <span class="op">*</span>settings<span class="op">;</span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span></span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a>  g_clear_object <span class="op">(&amp;</span>settings<span class="op">);</span></span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a>  settings <span class="op">=</span> g_settings_new <span class="op">(</span><span class="st">&quot;com.github.ToshioCP.tfe&quot;</span><span class="op">);</span></span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a>  g_settings_bind <span class="op">(</span>settings<span class="op">,</span> <span class="st">&quot;font&quot;</span><span class="op">,</span> fontbtn<span class="op">,</span> <span class="st">&quot;font&quot;</span><span class="op">,</span> G_SETTINGS_BIND_DEFAULT<span class="op">);</span></span>
<span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span> <span class="op">...</span></span>
<span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Static variable <code>settings</code> keeps a pointer to a GSettings
instance. Before application quits, the application releases the
GSettings instance. The function <code>g_clear_object</code> decreases
the reference count of the GSettings instance and assigns NULL to the
variable <code>settings</code>.</p>
<p>Startup handler creates GSettings instance with the schema id
“com.github.ToshioCP.tfe” and assigns the pointer to
<code>settings</code>. The function <code>g_settings_bind</code>
connects the settings keys (key and value) and the “font” property of
<code>fontbtn</code>. Then the two values will be always the same. If
one value changes then the other will automatically change.</p>
<p>For further information, refer to <a
href="https://docs.gtk.org/gio/class.Settings.html">GIO API rference –
GSettings</a>.</p>
<h2 id="build-with-meson">Build with Meson</h2>
<h3 id="build-and-test">Build and test</h3>
<p>Meson provides <code>gnome.compile_schemas</code> method to compile
XML file in the build directory. This is used to test the application.
Write the following to the <code>meson.build</code> file.</p>
<pre><code>gnome.compile_schemas(build_by_default: true, depend_files: &#39;com.github.ToshioCP.tfe.gschema.xml&#39;)</code></pre>
<ul>
<li><code>build_by_default</code>: If it is true, the target will be
build by default.</li>
<li><code>depend_files</code>: XML files to be compiled.</li>
</ul>
<p>In the example above, this method runs
<code>glib-compile-schemas</code> to generate
<code>gschemas.compiled</code> from the XML file
<code>com.github.ToshioCP.tfe.gschema.xml</code>. The file
<code>gschemas.compiled</code> is located under the build directory. If
you run meson as <code>meson _build</code> and ninja as
<code>ninja -C _build</code>, then it is under <code>_build</code>
directory.</p>
<p>After compilation, you can test your application like this:</p>
<pre><code>$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe</code></pre>
<h3 id="installation">installation</h3>
<p>It is a good idea to install your application in
<code>$HOME/bin</code> or <code>$HOME/.local/bin</code> directory. They
are local bin directories and work like system bin directories such as
<code>/bin</code>, <code>/usr/bin</code> or <code>usr/local/bin</code>.
You need to put <code>--prefix=$HOME</code> or
<code>--prefix=$HOME/.local</code> option to meson.</p>
<pre><code>$ meson --prefix=$HOME/.local _build</code></pre>
<p>If you want to install your application to a system bin directory,
for example <code>/usr/local/bin</code>, <code>--prefix</code> option
isn’t necessary.</p>
<p>Meson recognizes options like this:</p>
<table>
<thead>
<tr class="header">
<th style="text-align: left;">options</th>
<th style="text-align: left;">values (default)</th>
<th style="text-align: left;">values (–prefix=$HOME/.local)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">prefix</td>
<td style="text-align: left;">/usr/local</td>
<td style="text-align: left;">$HOME/.local</td>
</tr>
<tr class="even">
<td style="text-align: left;">bindir</td>
<td style="text-align: left;">bin</td>
<td style="text-align: left;">bin</td>
</tr>
<tr class="odd">
<td style="text-align: left;">datadir</td>
<td style="text-align: left;">share</td>
<td style="text-align: left;">share</td>
</tr>
<tr class="even">
<td style="text-align: left;">install directory</td>
<td style="text-align: left;">/usr/local/bin</td>
<td style="text-align: left;">$HOME/.local/bin</td>
</tr>
</tbody>
</table>
<p>The function <code>executable</code> needs <code>install: true</code>
to install your program.</p>
<pre><code>executable(&#39;tfe&#39;, sourcefiles, resources, dependencies: gtkdep, export_dynamic: true, install: true)</code></pre>
<p>However, you need to do one more thing. Copy your XML file to your
schema directory and execute <code>glib-compile-schemas</code> on the
directory.</p>
<ul>
<li><code>install_data</code> function copies a file into a target
directory.</li>
<li><code>gnome.post_install</code> function executes
‘glib-compile-schemas’ with an argument <code>schema_dir</code> as post
installation. This function is available since Meson 0.57.0. If the
version is earlier than that, use <code>meson.add_install_script</code>
instead.</li>
</ul>
<pre><code>schema_dir = get_option(&#39;prefix&#39;) / get_option(&#39;datadir&#39;) / &#39;glib-2.0/schemas/&#39;
install_data(&#39;com.github.ToshioCP.tfe.gschema.xml&#39;, install_dir: schema_dir)
gnome.post_install (glib_compile_schemas: true)</code></pre>
<p>The function <code>get_option</code> returns the value of build
options. See <a
href="https://mesonbuild.com/Reference-manual_functions.html#get_option">Meson
Reference Manual</a>. The operator ‘/’ connects the strings with ‘/’
separator.</p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">options</th>
<th style="text-align: left;">values (default)</th>
<th style="text-align: left;">values (–prefix=$HOME/.local)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">prefix</td>
<td style="text-align: left;">/usr/local</td>
<td style="text-align: left;">$HOME/.local</td>
</tr>
<tr class="even">
<td style="text-align: left;">datadir</td>
<td style="text-align: left;">share</td>
<td style="text-align: left;">share</td>
</tr>
<tr class="odd">
<td style="text-align: left;">schema_dir</td>
<td style="text-align: left;">/usr/local/share/glib-2.0/schemas</td>
<td style="text-align: left;">$HOME/.local/share/glib-2.0/schemas</td>
</tr>
</tbody>
</table>
<p>The source code of <code>meson.build</code> is as follows.</p>
<p>@@<span class="citation" data-cites="include">@include</span>
tfe6/meson.build @@@</p>
<p>Source files of <code>tfe</code> is under src/tfe6 directory. Copy
them to your temporary directory and compile and install it.</p>
<pre><code>$ meson --prefix=$HOME/.local _build
$ ninja -C _build
$ GSETTINGS_SCHEMA_DIR=_build:$GSETTINGS_SCHEMA_DIR _build/tfe # test
$ ninja -C _build install
$ ls $HOME/.local/bin
... ...
... tfe
... ...
$ ls $HOME/.local/share/glib-2.0/schemas
com.github.ToshioCP.tfe.gschema.xml
gschema.dtd
gschemas.compiled
... ...
$ tfe</code></pre>
<p>The screenshot is as follows.</p>
<figure>
<img src="image/tfe6.png" alt="tfe6" />
<figcaption aria-hidden="true">tfe6</figcaption>
</figure>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
  </html>
